package jp.shuitic.hatebwebhook.twitter.web.server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import com.google.appengine.api.users.User;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import jp.shuitic.hatebwebhook.twitter.web.client.HatebWebHookTwitException;
import jp.shuitic.hatebwebhook.twitter.web.client.HatebWebHookTwitUser;

/**
 * HatebWebHookTwitUserオブジェクトを操作する実装.<BR>
 * 単純に検索、更新しているのではなく、GAEのキャッシュを使用して負荷を軽減している。 
 * @author shuitic
 */
public class HatebWebHookTwitUserServiceImpl implements
		HatebWebHookTwitUserService {

	private User user = null;
	private Cache cache = null;
	
	/**
	 * コンストラクタ.<br/>
	 */
	public HatebWebHookTwitUserServiceImpl() {
        try {
            cache = CacheManager.getInstance().getCacheFactory().createCache(Collections.emptyMap());
        } catch (CacheException e) {
            throw new RuntimeException(e);
        }
	}
	

	/**
	 * コンストラクタ.<br/>
	 * Userを指定して作成する。
	 */
	public HatebWebHookTwitUserServiceImpl(User user) throws HatebWebHookTwitException {
		this.user = user;
        try {
            cache = CacheManager.getInstance().getCacheFactory().createCache(Collections.emptyMap());
        } catch (CacheException e) {
            throw new RuntimeException(e);
        }
	}
	
	
	/**
	 * AppEngineのユーザオブジェクトを指定して検索する
	 * @param user
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Override
	public HatebWebHookTwitUser findByCurrentUser() {
		HatebWebHookTwitUser hwtUser = (HatebWebHookTwitUser)cache.get(user.getEmail());
		if (hwtUser == null) {
			//キャッシュにない場合は検索
	        PersistenceManager pm = PMF.get().getPersistenceManager();
		    Query query = pm.newQuery(HatebWebHookTwitUser.class);
		    query.setFilter("user == userParam");
		    query.declareParameters("String userParam");
		    try {
		        List<HatebWebHookTwitUser> results =  (List<HatebWebHookTwitUser>) query.execute(user.getEmail());
		        if (results.size() > 0) {
		        	hwtUser = results.get(0);
		        	cache.put(hwtUser.getUser(), hwtUser);
		        	cache.put("hateb:" + hwtUser.getHatebUserName(),hwtUser);
		        }
		    } finally {
		        query.closeAll();
		        pm.close();
		    }
		}
		return hwtUser;
	}

	/**
	 * はてぶユーザ名を指定して検索する
	 * @param hatebUserName
	 * @return
	 */
	@Override
	public HatebWebHookTwitUser findByHatebUserName(String hatebUserName) {
		HatebWebHookTwitUser hwtUser = null;
		Object hwtUserCached = cache.get("hateb:" + hatebUserName);
		if (hwtUserCached == null) {
			//キャッシュにない場合は検索
	        PersistenceManager pm = PMF.get().getPersistenceManager();
		    Query query = pm.newQuery(HatebWebHookTwitUser.class);
		    query.setFilter("hatebUserName == hatebUserNameParam");
		    query.declareParameters("String hatebUserNameParam");
		    try {
		        List<HatebWebHookTwitUser> results =  (List<HatebWebHookTwitUser>) query.execute(hatebUserName);
		        if (results.size() > 0) {
		        	hwtUser = results.get(0);
		        	cache.put(hwtUser.getUser(), hwtUser);
		        	cache.put("hateb:" + hwtUser.getHatebUserName(),hwtUser);
		        }
		    } finally {
		        query.closeAll();
		        pm.close();
		    }
		} else if (hwtUserCached.equals("")) {
			//キャッシュが見つかったけど、空文字だった場合はNULL
			return null;
		} else {
			//キャッシュが見つかった場合
			return (HatebWebHookTwitUser)hwtUserCached;
		}
		
		if (hwtUser == null) {
			//見つからなかった場合は空文字をキャッシュに入れる
			cache.put("hateb:" + hatebUserName, "");
			return null;
		} else {
			//DataStoreから見つかった場合
			return hwtUser;
		}
	}

	/**
	 * データを削除する
	 * @param user
	 */
	@Override
	public boolean removeCurrentUser() {
    	//キャッシュからとってきたものは削除できないので、DataStoreから取得する
        PersistenceManager pm = PMF.get().getPersistenceManager();
	    Query query = pm.newQuery(HatebWebHookTwitUser.class);
	    query.setFilter("user == userParam");
	    query.declareParameters("String userParam");
        try {
	        List<HatebWebHookTwitUser> results =  (List<HatebWebHookTwitUser>) query.execute(user.getEmail());
	        if (results.size() > 0) {
	        	HatebWebHookTwitUser hwtUser = (HatebWebHookTwitUser)results.get(0);
				//キャッシュから削除
				cache.remove(hwtUser.getUser());
				cache.put("hateb:" + hwtUser.getHatebUserName(), "");
				//データから削除
	        	pm.deletePersistent(hwtUser);
				return true;
			} else {
				return false;
			}
        } finally {
        	pm.close();
        }
	}

	/**
	 * 保存する
	 * @param hatebWebHookTwitUser
	 */
	@Override
	public void save(HatebWebHookTwitUser hatebWebHookTwitUser)
			throws Exception {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
        	pm.makePersistent(hatebWebHookTwitUser);
        } finally {
        	pm.close();
        }
	}

	/**
	 * パラメータを検証してオブジェクトを返す
	 * @param hatebUserName はてぶユーザ命
	 * @param hatebWebHookKey　WebHookKey
	 * @param twitterUser　twitterユーザ名
	 * @param twitterPassword twitterパスワード
	 * @return
	 * @throws Exception
	 */
	@Override
	public HatebWebHookTwitUser validate(String hatebUserName,
			String hatebWebHookKey, String twitterUser, String twitterPassword)
			throws Exception {
		HatebWebHookTwitUser hwtUser = this.findByCurrentUser();
		//共通チェック処理
		ArrayList<String> errMsg = new ArrayList<String>();
		if (isEmpty(hatebUserName)) {
			errMsg.add("はてなブックマークユーザ名を指定してください");
		}
		if (isEmpty(hatebWebHookKey)) {
			errMsg.add("ウェブフックキーを指定してください");
		}
		if (isEmpty(twitterUser)) {
			errMsg.add("Twitterユーザ名を指定してください");
		}
		if (isEmpty(twitterPassword)) {
			errMsg.add("Twitterパスワードを指定してください");
		}
		//Twiterユーザ名とパスワードを調べる
		if (errMsg.size() == 0) {
			if (!validTwitterUserPassword(twitterUser, twitterPassword)) {
				errMsg.add("Twitterユーザ名とパスワードが正しくありません");
			}
		}
		//はてなブックマークのユーザが既に登録ずみかどうか調べる
		if (errMsg.size() == 0) {
			HatebWebHookTwitUser sameHatebUser = this.findByHatebUserName(hatebUserName);
			if ((sameHatebUser !=null) && (!sameHatebUser.getUser().equals(this.user.getEmail()))) {
				//すでに存在し、ユーザがログインしているユーザと違う場合
				errMsg.add("はてなブックマークユーザ「"+ hatebUserName +"」は、すでに別のユーザにより使用されています");
			}
		}
		//エラーがある場合、例外を返す
		if (errMsg.size() != 0) {
			throw new HatebWebHookTwitException(errMsg);
		}
		
		//オブジェクトの作成および更新
		if (hwtUser == null) {
			//新規の場合、オブジェクトを作成する
			hwtUser = new HatebWebHookTwitUser();	
		}
		hwtUser.setHatebUserName(hatebUserName);
		hwtUser.setHatebWebHookKey(hatebWebHookKey);
		hwtUser.setTwitterUserName(twitterUser);
		hwtUser.setTwitterPassword(twitterPassword);
		hwtUser.setUser(user.getEmail());
		hwtUser.setEnable(true);
		
		return hwtUser;
	}
	
	private static boolean isEmpty(String value) {
		return ((value == null) || value.equals(""));
	}
	
	/**
	 * Twitterユーザが妥当かどうかチェックする
	 * @param twtUser
	 * @param twtPass
	 * @return
	 * @throws TwitterException 
	 */
	private static boolean validTwitterUserPassword(String twtUser, String twtPass) throws TwitterException {
		Twitter tiwtter = new Twitter(twtUser, twtPass);
		try {
			twitter4j.User twitterUser = tiwtter.verifyCredentials();
			return (twitterUser != null);
		} catch (TwitterException ex) {
			if (ex.getStatusCode()== 401) {
				return false;
			} else {
				throw ex;
			}
		}
	}

	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	
}
